package Ants.Modules.Fight.Minimax;

import java.util.List;

import javax.management.RuntimeErrorException;

import org.apache.log4j.Logger;

import Ants.Model.Alert;
import Ants.Model.AlertType;

public class Minimax {

	private static Logger Log = Logger.getLogger(Minimax.class);
	private static int CutoffCounter;
	
	private long minimaxStartTime;
	private Alert alert;
	private int cutoff;
	private int timeout;
	
	public Minimax(int cutoff, Alert alert, int timeout){
		this.cutoff = cutoff;
		this.alert = alert;
		this.timeout = timeout;	
	}

	public MinimaxStatus DoMinimax(MinimaxStatus minimaxStatus){
		this.minimaxStartTime = System.currentTimeMillis();
		return evaluateMax(minimaxStatus, Integer.MIN_VALUE, Integer.MAX_VALUE);
	}

	private MinimaxStatus evaluateMax(MinimaxStatus minimaxStatus, int alfa, int beta){

		if(minimaxStatus.getLevel() == cutoff){
			int value = evaluate(minimaxStatus);
			minimaxStatus.setValue(value);
			return minimaxStatus;
		}

		MinimaxStatus maxStatus = null;

		List<MinimaxStatus> successors = minimaxStatus.getSussessorsMax();

		int max = Integer.MIN_VALUE;

		for (MinimaxStatus sussessor : successors){

			if((System.currentTimeMillis() - minimaxStartTime) > timeout && maxStatus!=null){
				Log.debug(String.format("Cutoff #%s! current time is %s ms but max time is %s ms",++CutoffCounter,(System.currentTimeMillis() - minimaxStartTime),timeout));
				return maxStatus;
			}
		
			sussessor.setLevel(minimaxStatus.getLevel()+1);
			MinimaxStatus minStatus = evaluateMin(sussessor, alfa, beta);

			int value = minStatus.getValue();

			if(value > max){
				max = value;
				sussessor.setValue(value);
				maxStatus = sussessor;
			}

			if(max >= beta)
				break;

			//aggiorno alfa
			alfa = Math.max(max, alfa);
		}
		
		return maxStatus;
	}

	private MinimaxStatus evaluateMin(MinimaxStatus minimaxStatus, int alfa, int beta){

		//		Il livello terminale DEVE essere solo un nodo max		
		//
		//		if(minimaxStatus.getLevel() >= cutoff){
		//			int value = Evaluate(minimaxStatus);
		//			minimaxStatus.setValue(value);
		//			return minimaxStatus;
		//		}

		int min = Integer.MAX_VALUE;
		MinimaxStatus minStatus = null;
		List<MinimaxStatus> successors = minimaxStatus.getSussessorsMin();

		for (MinimaxStatus sussessor : successors){

			if((System.currentTimeMillis() - minimaxStartTime) > timeout && minStatus!=null){
				return minStatus;
			}
			
			sussessor.setLevel(minimaxStatus.getLevel()+1);
			MinimaxStatus maxStatus = evaluateMax(sussessor, alfa, beta);
			int value = maxStatus.getValue();

			if(value < min){
				min = value;
				sussessor.setValue(min);
				minStatus = sussessor;
			}

			if(min <= alfa)
				break;

			beta = Math.min(min,beta);
		}

		return minStatus;
	}

	private int evaluate(MinimaxStatus minimaxStatus){
		
		AlertType type = alert.getType();
		MinimaxEvaluator evaluator = new MinimaxEvaluator(minimaxStatus);
		
		switch(type){
			case ENEMY:{
				minimaxStatus.calculateAntsDistances();
				return evaluator.evaluateEnemy();
			}
			case ENEMY_WITH_FOOD:{
				minimaxStatus.calculateAntsDistances();
				minimaxStatus.calculateFoodDistances();
				return evaluator.evaluateEnemyWithFood();
			}
			case ENEMY_HILL:{
				minimaxStatus.calculateAntsDistances();
				minimaxStatus.calculateMyDistanceFromEnemyHill();
				minimaxStatus.calculateFoodDistances();
				return evaluator.evaluateEnemyWithHill();
			}
			case ENEMY_CLOSE_TO_MY_HILL:{
				minimaxStatus.calculateAntsDistances();
				minimaxStatus.calculateEnemyDistanceFromMyHill();
				return evaluator.evaluateEnemyCloseToMyHill();
			}
		}
		
		throw new RuntimeErrorException(new Error("Unknown AlertType"));
	}
	
	public static int GetCutoffCounter() {
		return CutoffCounter;
	}

	public static void SetCutoffCounter(int cutoffCounter) {
		CutoffCounter = cutoffCounter;
	}
}
